/**
 * Description: 基于UDP协议的群聊聊天室服务端
 * File: server.c |Time: 2021-04-14 17:15
 */

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>

#define BUF_SIZE    100
#define NAME_SIZE   20

#define ERR(msg) do {   \
    perror(msg);        \
    printf("error at: %s:%s:%d\n", __FILE__, __func__, __LINE__); \
    exit(EXIT_FAILURE); \
} while (0)

typedef struct sockaddr *skaddr;


// 消息结构体
typedef struct {
    int len;
    char name[NAME_SIZE];
    char data[BUF_SIZE];
} msg_pack;

// 用户链表
typedef struct user_node {
    struct user_node *next;
    struct sockaddr_in client;
} user_link;


// 存储服务端socket信息的结构体
typedef struct {
    int sockfd;
    struct sockaddr_in *addr_p;
    user_link *L;
} sock_info;

char username[NAME_SIZE] = "服务器";

// 子线程
void *thread_input(void *p);
// 创建链表
user_link *link_create(void);
// 判断节点是否存在
int link_exist(user_link *L, struct sockaddr_in *addr_p);
// 插入节点
void link_insert(user_link *L, struct sockaddr_in *addr_p);
// 移除节点
void link_remove(user_link *L, struct sockaddr_in *addr_p);
// 循环节点发送消息
void link_notice(user_link *L, int sockfd,  struct sockaddr_in *addr_p, msg_pack *msg);

int main(int argc, char *argv[])
{
    if (argc < 3) {
        printf("Usage %s <ip> <port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd) {
        ERR("socket");
    }

    struct sockaddr_in serveraddr, clientaddr;
    socklen_t addrlen = sizeof(clientaddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    if (-1 == bind(sockfd, (skaddr)&serveraddr, sizeof(serveraddr)))
    {
        ERR("bind");
    }

    // 创建用户链表
    user_link *L = link_create();

    // 创建子线程阻塞输入
    int err_number;
    pthread_t tid;
    sock_info client_info;
    client_info.sockfd = sockfd;
    client_info.addr_p = &clientaddr;
    client_info.L = L;
    err_number = pthread_create(&tid, NULL, thread_input, &client_info);
    if (0 != err_number) {
        strerror(err_number);
        exit(EXIT_FAILURE);
    }

    // 服务端主线程接收并打印信息
    // 子线程发送信息
    msg_pack msg, sysmsg;
    strcpy(sysmsg.name, username);

    while (1)
    {
        memset(&msg, 0, sizeof(msg_pack));
        if (-1 == recvfrom(sockfd, &msg, sizeof(msg_pack), 0, (skaddr)&clientaddr, &addrlen)) {
            ERR("recvfrom");
        }

        // 循环链表判断是否上线
        if (0 == strncmp(msg.data, "online", 6)) {
            bzero(sysmsg.data, BUF_SIZE);
            
            sprintf(sysmsg.data, "*** %s 已上线 ***", msg.name);
            printf("%s\n", sysmsg.data);
            link_insert(L, &clientaddr);
            link_notice(L, sockfd, &clientaddr, &sysmsg);
        } else if (0 == strncmp(msg.data, "quit", 4)) {
            bzero(sysmsg.data, BUF_SIZE);
            sprintf(sysmsg.data, "*** %s 已下线 ***", msg.name);
            printf("%s\n", sysmsg.data);
            link_notice(L, sockfd,  &clientaddr, &sysmsg);
            link_remove(L, &clientaddr);
            continue;
        } else {
            // 群发消息
            link_notice(L, sockfd,  &clientaddr, &msg);
            printf("[%s] %s\n",msg.name, msg.data);
        }

   }
    

    return 0;
}

void *thread_input(void *p)
{
    msg_pack msg;
    strcpy(msg.name, username);
    sock_info *client_info = (sock_info *)p;
    int sockfd = client_info->sockfd;
    user_link *L = client_info->L;
    struct sockaddr_in *addr_p = client_info->addr_p;
    while (1)
    {
        bzero(msg.data, BUF_SIZE);
        if (NULL == fgets(msg.data, BUF_SIZE, stdin)) {
            ERR("fgets");
        }

        msg.len = strlen(msg.data);

        link_notice(L, sockfd, addr_p, &msg);
    }


    return NULL;
}


user_link *link_create(void)
{
    user_link *L = (user_link *)malloc(sizeof(user_link));
    L->next = NULL;
    memset(&L->client, 0, sizeof(L->client));

    return L;
}


// 判断节点是否存在 1=存在 0=不存在
int link_exist(user_link *L, struct sockaddr_in *addr_p)
{
    user_link *cur = L;
    while (cur->next) 
    {
        if (cur->next->client.sin_port == addr_p->sin_port &&
                cur->next->client.sin_addr.s_addr == addr_p->sin_addr.s_addr) 
        {
            return 1;
        }
        cur = cur->next;
    }

    return 0;
}


void link_insert(user_link *L, struct sockaddr_in *addr_p)
{
    user_link *new = (user_link *)malloc(sizeof(user_link));
    new->client = *addr_p;
    new->next = L->next;
    L->next = new;
}


void link_remove(user_link *L, struct sockaddr_in *addr_p)
{
    user_link *cur = L;
    user_link *cmp;

    while(cur->next)
    {
        // 比较IP地址和端口号， 遇到相同的删除
        cmp = cur->next;

        if (cmp->client.sin_addr.s_addr == addr_p->sin_addr.s_addr &&
                cmp->client.sin_port == addr_p->sin_port) 
        {
            cur->next = cmp->next;
            free(cmp);
            return;
        }

        cur = cur->next;
    }
}


void link_notice(user_link *L, int sockfd,  struct sockaddr_in *addr_p, msg_pack *msg)
{
    user_link *cur = L;

    while (cur->next)
    {
        cur = cur->next;
        if (strcmp(msg->name, username) != 0 &&
            cur->client.sin_port == addr_p->sin_port &&
            cur->client.sin_addr.s_addr == addr_p->sin_addr.s_addr)
        {
            continue;
        }

        if (-1 == sendto(sockfd, msg, sizeof(msg_pack), 0, (skaddr)&cur->client, sizeof(struct sockaddr))) 
        {
            ERR("sendto");
        }
 
    }
}

